package org.metasketch.simulator.runtime;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.metasketch.logger.Log;
import org.metasketch.model.CommonIdentified;
import org.metasketch.simulator.configuration.loaders.DynamicResourceConfig;
import org.metasketch.simulator.configuration.loaders.SimpleTransaction;
import org.metasketch.simulator.configuration.loaders.Transaction;
import org.metasketch.simulator.petri.PetriNet;
import org.metasketch.simulator.tools.generic.Variable;
import org.metasketch.simulator.transformation.PetriNetSimulationPackage;

public class ArrivalsManager {

	ProcessManager manager;

	PetriNetSimulationPackage simulPackage;
	DynamicResourceConfig dynamicResourceConfig;

	Map<Transaction, Long> nextArrivalTime = new HashMap<Transaction, Long>();

	public void initialize() {
		List<Transaction> trans = dynamicResourceConfig.getTransactions();
		for (Transaction transaction : trans) {
			generateNextArrivalTime(transaction, 0l);
		}
	}

	private Long generateNextArrivalTime(Transaction transaction,
			Long currentTime) {
		if (transaction instanceof SimpleTransaction) {
			SimpleTransaction simple = (SimpleTransaction) transaction;

			Long time = ExpressionEngine.getLong(simple.getArrivalExpression());

			time += currentTime;

			nextArrivalTime.put(transaction, time);

			return time;

		}
		return null;
	}

	public void createNewArrivals(Long currentTime) {

		List<Transaction> trans = dynamicResourceConfig.getTransactions();
		for (Transaction transaction : trans) {
			Long time = nextArrivalTime.get(transaction);

			// this is so that multiple arrivals can occur in the same timestep
			while (time.compareTo(currentTime) == 0) {
				createArrival(transaction, currentTime);
				time = generateNextArrivalTime(transaction, currentTime);
			}

		}

	}

	private void createArrival(Transaction transaction, Long currentTime) {
		SimulationProcess process = new SimulationProcess();

		PetriNet petriNet = simulPackage.getPetriNets().getByInitialIdentifier(
				transaction.getId());
		process.setInitialPetriNet(petriNet);

		// put resource or not...
		// if (transaction.isCreateResource()) {

		process.getOwnedResources().addResource(
				transaction.getActorRoleResource(), 1l);
		// }
		// else....

		if (transaction instanceof SimpleTransaction) {
			SimpleTransaction st = (SimpleTransaction) transaction;

			Binding binding = new Binding();
			// binding.setVariable("semantic", semantic);
			// binding.setVariable("type", semanticType);

			List<Variable> variables = st.getVariables();
			for (Variable variable : variables) {

				Object value = ExpressionEngine.get(binding,
						variable.getValue());

				process.getAttributes().put(variable.getName(), value);
				binding.setVariable(variable.getName(), value);
			}

		}

		manager.addProcess(process, currentTime);

	}

	public ProcessManager getManager() {
		return manager;
	}

	public void setManager(ProcessManager manager) {
		this.manager = manager;
	}

	public DynamicResourceConfig getDynamicResourceConfig() {
		return dynamicResourceConfig;
	}

	public void setDynamicResourceConfig(
			DynamicResourceConfig dynamicResourceConfig) {
		this.dynamicResourceConfig = dynamicResourceConfig;
	}

	public PetriNetSimulationPackage getSimulPackage() {
		return simulPackage;
	}

	public void setSimulPackage(PetriNetSimulationPackage simulPackage) {
		this.simulPackage = simulPackage;
	}

}
